﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Networking.Proximity;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using System.Runtime.InteropServices.WindowsRuntime;
using System.IO;
using Windows.Devices.Bluetooth.Rfcomm;
using Windows.Devices.Bluetooth;
using Windows.Devices.Enumeration;
using System.Diagnostics;
using System.Globalization;


namespace Bluefly
{
    public abstract class VarioServiceBase : IVarioService
    {


        public const int FLIGHT_STATUS_NONE = 0;
        public const int FLIGHT_STATUS_FLYING = 1;


        private KalmanFilteredAltitude altitude;
        private double temperature;
        private double battery;
        private DataBuffer dataBuffer;


        private bool _hasPressure;
   
        private bool flagPitotCalibrated;
        private int pitotCalibrateCount;
        private double pitotCalibration;
        private double pressureDiff;
        private double pitotSpeed;
        private ConnectionState mState;

        public event EventHandler<PressureEventArgs> PressureUpdated;

        public event EventHandler<BatteryEventArgs> BatteryVoltageUpdated;

        public event EventHandler<TemperatureEventArgs> TemperatureUpdated;

        public event EventHandler<ConnectionStateEventArgs> ConnectionStateUpdated;

        public event EventHandler<PositionEventArgs> PositionUpdated;

        public enum UpdateType
        {
            NONE = 0,
            PRS = 1,
            TMP = 2,
            VER = 3,
            BAT = 4,
            KEYS = 5,
            VALUES = 6
        }

        protected PiecewiseLinearFunction chargeFromVolts;

        public VarioServiceBase()
        {
            _errors = new List<string>();
            mState = ConnectionState.STATE_NONE;
            referenceQNH = KalmanFilteredAltitude.STANDARD_SEA_LEVEL_PRESSURE;

            timeInterval = 0.02D;
            initChargeFunction();
            setUpData();
            Current = this;
        }


        void initChargeFunction()
        {
            chargeFromVolts = new PiecewiseLinearFunction(new Point2d(3.6, 0.0));
            chargeFromVolts.addNewPoint(new Point2d(3.682, 0.032));
            chargeFromVolts.addNewPoint(new Point2d(3.696, 0.124));
            chargeFromVolts.addNewPoint(new Point2d(3.75, 0.212));
            chargeFromVolts.addNewPoint(new Point2d(3.875, 0.624));
            chargeFromVolts.addNewPoint(new Point2d(3.96, 0.73));
            chargeFromVolts.addNewPoint(new Point2d(4.16, 1.0));
        }


        double referenceQNH; 

        protected  void setUpData()
        {

            altitude = new KalmanFilteredAltitude(referenceQNH, "KFA");

            List<IDatasource> dataSources = new List<IDatasource>();
            dataSources.Add(altitude);
            dataSources.Add(altitude.KalmanVario);
            dataSources.Add(altitude.DampedVario);

            SharedPreferences sharedPrefs = BFVSettings.sharedPrefs;

            int bufferSize = int.Parse(sharedPrefs.getString("display_varioBufferSize", "500"));
            int bufferRate = int.Parse(sharedPrefs.getString("display_varioBufferRate", "3"));

            dataBuffer = new DataBuffer(dataSources, bufferSize, bufferRate);
        }


        protected void setState(ConnectionState state)
        {

                var tmp = mState;
                mState = state;
                RaiseConnectionStateChanged(state, tmp);
       
        }


        void firstPressure()
        {

            System.Threading.Tasks.Task.Factory.StartNew(() =>
            {
                try
                {
                    calibratePitot();

                    System.Threading.Tasks.Task.Delay(200).Wait();

                    _hasPressure = true;
                    setState(ConnectionState.STATE_CONNECTEDANDPRESSURE);
                }
                catch (Exception e)
                {

                }
            });

        }

        protected double _rawAltitude;

        public double SetQNH(double qnh)
        {
            if (altitude != null)
            {
                referenceQNH = qnh;
                return altitude.setSeaLevelPressure(qnh);
            }

            return -9999;
        }


        public double SetAltitude(double alt)
        {
            return altitude == null ? -9999 : altitude.setAltitude(alt);
        }


        protected void updatePressure(int pressure, double time)
        {

            _rawAltitude = altitude.addPressure((double)pressure, time);
            _dampedAltitude = altitude.DampedAltitude;
            _dampedrate = altitude.DampedVario.Value;
            _kalmanrate = altitude.KalmanVario.Value;

            if (dataBuffer != null)
            {
                dataBuffer.addData();
            }
        }

        protected void updatePitotPressures(int pitotPressure, int staticPressure)
        {

            if (flagPitotCalibrated)
            {
                pressureDiff = 0.95 * pressureDiff + 0.05 * (pitotPressure - staticPressure - pitotCalibration);
                double root = 2 * pressureDiff / 1.2754;
                if (root < 0)
                {
                    root = 0.0;
                }
                pitotSpeed = Math.Sqrt(root);
            }
            else
            {
                pitotCalibrateCount++;
                if (pitotCalibrateCount > 50)
                {
                    if (pitotCalibrateCount <= 500)
                    {
                        pitotCalibration += (pitotPressure - staticPressure);
                    }
                    else
                    {
                        pitotCalibration = pitotCalibration / 450.0;
                        pitotCalibrateCount = 0;
                        flagPitotCalibrated = true;
                    }
                }
            }
        }


        protected void calibratePitot()
        {
            flagPitotCalibrated = false;
            pitotCalibrateCount = 0;
            pitotSpeed = 0;
            pressureDiff = 0;
        }

        bool hasPressure()
        {
            return _hasPressure;
        }

        protected volatile bool _isConnected;

        public bool IsConnected
        {
            get { return _isConnected; }
        }


        public void Stop()
        {
            _isConnected = false;
        }


        public int HardwareVersion
        {
            get;
            protected set;

        }

        public abstract void Run();

        protected abstract void Disconnect();

        protected List<string> _errors;

        public void ClearErrors()
        {
            lock (_errors)
            {
                _errors.Clear();

            }
        }


        protected void AddError(string error)
        {
            lock (_errors)
            {
                _errors.Add(error);
            }
        }

        public IEnumerable<string> Errors
        {
            get
            {
                lock (_errors)
                {
                    foreach (var error in _errors)
                    {
                        yield return error;

                    }
                }
            }
        }


        public double ReferenceAltitude
        {
            get { return _rawAltitude; }
        }

        protected bool _firstPressure;

        public static IVarioService Current
        {
            get;
            private set;
        }

        protected UpdateType lastUpdateType;


        private double time;
        private double timeInterval;

        protected int _rawPressure;

        protected double _dampedrate;
        protected double _kalmanrate;




        protected virtual void updateTemperature(int temperature)
        {
            this.temperature = (double)(temperature / 10.0);

        }


        void updateBattery(double battery)
        {
            this.battery = battery;
        }

        double getBattery()
        {
            return battery;
        }


        void _setAltitude(KalmanFilteredAltitude altitude)
        {
            this.altitude = altitude;
            this.setUpData();
        }


        double getPitotSpeed()
        {
            if (flagPitotCalibrated)
            {
                return pitotSpeed;
            }
            else
            {
                return -99.0;
            }
        }


        protected double _dampedAltitude;


        protected abstract void OnDeviceSentenceReceived(string line);

        protected virtual void  ProcessPressureUpdate(int pressure , int? pitotPressure )
        {

            if (this.lastUpdateType == UpdateType.BAT)
            {
                time = 0.04;
            }
            else
            {
                time = 0.02;
            }

            this.lastUpdateType = UpdateType.PRS;

            _rawPressure = pressure; 

            updatePressure(_rawPressure, time);

            if (!_firstPressure)
            {
                firstPressure();
                _firstPressure = true;
            }

            if (pitotPressure.HasValue)
            {
                updatePitotPressures(pitotPressure.Value, _rawPressure);
            }
        }


        protected virtual void ProcessPitotUpdate(int pitotPressure)
        {

        }

        protected virtual void RaiseTemperatureChanged(int temperature)
        {
            var evt = this.TemperatureUpdated;
            if (evt != null)
            {
                evt(this, new TemperatureEventArgs(temperature));
            }
        }


        protected virtual void RaiseBatteryVoltageChanged(double charge)
        {
            var evt = this.BatteryVoltageUpdated;
            if (evt != null)
                evt(this, new BatteryEventArgs(charge));

        }


        protected virtual void RaiseConnectionStateChanged(ConnectionState state, ConnectionState oldstate)
        {

            var evt = this.ConnectionStateUpdated;
            if (evt != null)
                evt(this, new ConnectionStateEventArgs(state, oldstate));

        }


        protected virtual void RaisePressureChanged(int pressure, double dampedaltitude, double rawAltitude, double dampedrate, double kalmanrate)
        {
            var evt = PressureUpdated;
            if (evt != null)
                evt(this, new PressureEventArgs(pressure, dampedaltitude, rawAltitude, dampedrate, kalmanrate));
        }

        protected void updateHardwareSettingsValues(string line)
        {
            //throw new NotImplementedException();
        }

        protected void updateHardwareSettingsKeys(string line)
        {
            //throw new NotImplementedException();
        }


        protected void setHardwareVersion(int ver)
        {
            HardwareVersion = ver;
        }





        
    }

}
